﻿using PSE.Framework.Communication.Configuration.Adapters;
using System.Threading;
using TGPInterfaceParam;
using System;
using PSE.Framework.Communication.Adapters.Tgp;
using PSE.Framework.Communication.Configuration.Tgp;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Text;
using System.Xml;

namespace PSE.Framework.Communication.Adapters.Tgp
{
    public class TgpOutputAdapter : ResponseOutputAdapter<TgpOutputConfiguration, TgpOpenParameter>
    {
        #region Construtores
        private TGPExecutorNet.TGPExecutor _tgpExecutor;
        private TgpOpenParameter _openParameter;
        private TgpCommon _common;
        private Dictionary<string, XmlSerializer> _serializers;

        public TgpOutputAdapter()
        {

        }
        #endregion

        #region Open
        public override void Open(TgpOutputConfiguration config, TgpOpenParameter parameter)
        {
            if (config == null)
                throw new ArgumentNullException("config");
            if (parameter == null)
                throw new ArgumentNullException("parameter");

            this.VerifyIsCreated();

            // Criando instância dos serializers
            _serializers = new Dictionary<string, XmlSerializer>();

            // Criando instância do executor
            _tgpExecutor = new TGPExecutorNet.TGPExecutor();
            // Salvando parâmetro de abertura
            _openParameter = parameter;
            // Criando instância do common
            _common = new TgpCommon(_tgpExecutor, _openParameter, config.AuthenticationRetry, config.AuthenticationRetryWait);

            // Abrindo TGP
            _common.Open();

            base.Open(config, parameter);
        }
        #endregion

        #region Send
        protected override object Send(object message)
        {
            TGPInterfaceParam.ParametroEntrada paramEntrada = new TGPInterfaceParam.ParametroEntrada();

            TGPInterfaceParam.ParametroSaida paramSaida;
            TGPInterfaceParam.ColRespMensagem colResposta;
            TGPInterfaceParam.RespMensagem resposta;

            // Preenchendo parâmetros de entrada
            paramEntrada.InstituicaoOrigem = _openParameter.InstituicaoOrigem;
            paramEntrada.InstituicaoDestino = _openParameter.InstituicaoDestino;
            paramEntrada.Usuario = _openParameter.Usuario;
            paramEntrada.Sistema = _openParameter.Sistema;
            paramEntrada.TipoComando = TGPInterfaceParam.TipoComando.TCPutMsg;
            paramEntrada.TipoMensagem = TGPInterfaceParam.TipoMensagem.TMRequisicao;
            paramEntrada.Mensagem = (string)message;

            // Enviando mensagem
            _tgpExecutor.Executa(paramEntrada, out paramSaida);

            colResposta = paramSaida.ColRespMensagem;

            if (colResposta.Count == 0)
                throw new InvalidOperationException("Invalid response, ColRespMensagem is empty");

            // Capturando resposta
            resposta = colResposta[0];

            TgpResponseMessage response = new TgpResponseMessage();

            response.InstituicaoOrigem = resposta.InstituicaoOrigem;
            response.InstituicaoDestino = resposta.InstituicaoDestino;
            response.NumeroOperacao = resposta.Nuop;
            response.CodigoMensagemServidor = resposta.CodMsgServer;

            response.IdMensagem = resposta.MsgId;
            response.Mensagem = resposta.Mensagem;
            response.Sistema = resposta.Sistema;
            response.Usuario = resposta.Usuario;

            response.TipoMensagem = TgpMessageType.Requisicao;

            return response;
        }
        #endregion

        #region Serialize
        protected override object Serialize(object message, string action)
        {
            object serializedMessage = message;

            // Verifica se mensagem não é uma string
            if (!(message is string))
            {
                // Verifica se já existe um serializer para esta action
                if (!_serializers.ContainsKey(action))
                {
                    // Se não existir, cria serializer
                    _serializers.Add(action, new XmlSerializer(message.GetType()));
                }

                StringBuilder sb = new StringBuilder();

                XmlWriterSettings settings = new XmlWriterSettings();
                // Identa o xml
                settings.Indent = true;
                // Encoding UTF-8
                settings.Encoding = System.Text.Encoding.UTF8;

                // Criando writer
                using (XmlWriter writer = XmlWriter.Create(sb, settings))
                {
                    // Remove as namespaces da tag root
                    XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
                    namespaces.Add(string.Empty, string.Empty);

                    // Serializando objeto
                    _serializers[action].Serialize(writer, message, namespaces);

                    serializedMessage = sb.ToString();
                }
            }

            return serializedMessage;
        }
        #endregion

        #region Deserialize
        protected override object Deserialize(object message, string action)
        {
            // Não deserializa porque vai retornar um TGPResponseMessage
            return message;
        }
        #endregion

        #region SendEchoMessage
        /// <summary>
        /// Envia mensagem de eco
        /// </summary>
        /// <returns>Código da mensagem</returns>
        public int SendEchoMessage()
        {
            this.VerifyIsOpened();

            return _common.SendEchoMessage();
        }
        #endregion

        #region GetEchoMessageStatus
        /// <summary>
        /// Captura o status da mensagem de eco
        /// </summary>
        /// <param name="codigoMensagem">Código da mensagem</param>
        /// <returns>Status da mensagem</returns>
        public TgpMessageStatus GetEchoMessageStatus(int codigoMensagem)
        {
            this.VerifyIsOpened();

            return _common.GetEchoMessageStatus(codigoMensagem);
        }
        #endregion

        #region ConfirmServerMessageReceive
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="codigoMensagem"></param>
        ///// <returns></returns>
        //public bool ConfirmServerMessageReceive(int codigoMensagem)
        //{
        //    this.VerifyIsOpened();

        //    if (codigoMensagem <= 0)
        //        throw new ArgumentOutOfRangeException("codigoMensagem");

        //    return _tgpExecutor.ConfirmaRecebimentoMensagemServer(codigoMensagem);
        //}

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="codigoMensagem"></param>
        ///// <param name="tipoMensagem"></param>
        ///// <returns></returns>
        //public bool ConfirmServerMessageReceive(int codigoMensagem, TGPMessageType tipoMensagem)
        //{
        //    this.VerifyIsOpened();

        //    if (codigoMensagem <= 0)
        //        throw new ArgumentOutOfRangeException("codigoMensagem");

        //    return _tgpExecutor.ConfirmaRecebimentoMensagemServer_v2(codigoMensagem, tipoMensagem.ToString("d"), _openParameter.Usuario, _openParameter.Sistema, _openParameter.InstituicaoOrigem);
        //}
        #endregion

        #region GetMessageStatus
        /// <summary>
        /// Captura o status de uma mensagem
        /// </summary>
        /// <param name="numeroOperacao">Número da operação da mensagem</param>
        /// <returns>Status da mensagem</returns>
        public TgpMessageStatus GetMessageStatus(string numeroOperacao)
        {
            this.VerifyIsOpened();

            if (string.IsNullOrEmpty(numeroOperacao))
                throw new ArgumentNullException("numeroOperacao");

            int iRetorno = _tgpExecutor.ConsultaStatus(numeroOperacao, _openParameter.InstituicaoOrigem, _openParameter.InstituicaoDestino, _openParameter.Usuario, _openParameter.Sistema);

            if (Enum.IsDefined(typeof(TgpMessageStatus), iRetorno))
                return (TgpMessageStatus)iRetorno;
            else
                return TgpMessageStatus.Indefinido;
        }
        #endregion

        #region Close
        public override void Close()
        {
            this.VerifyIsOpened();

            // Fechando TGP
            _common.Close();

            base.Close();
        }
        #endregion
    }
}
